// $Id: tmp.cpp 2 2009-09-02 08:47:42Z joseph2002 $
/*
 * Copyright 2009 Joseph Wu
 * Distributed under the Boost Software License, Version 1.0.
 * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
 *
 * For more information, see http://xpcomplus.sourceforge.net
 */

#include <iostream>
#include <boost/test/unit_test.hpp>
#include <boost/mpl/for_each.hpp>
#include <boost/mpl/placeholders.hpp>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/transform_view.hpp>
#include <boost/mpl/zip_view.hpp>
#include <boost/mpl/aux_/na.hpp>
#include <boost/type_traits/add_pointer.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <xpcom/interface_table.hpp>
#include <xpcom/object.hpp>
#include <xpcom/result_code.hpp>
#include "nsIAtom_impl.hpp"
#include "nsISerializable_impl.hpp"
#include "nsILocalFile_impl.hpp"
#include "nsIClassInfo.hpp"
#include "nsISupportsChar_impl.hpp"
#include "nsISupportsVoid_impl.hpp"
#include "nsISupportsCString_impl.hpp"
#include "nsISupportsString_impl.hpp"
#include "nsISupportsDouble_impl.hpp"
#include "nsISupportsFloat_impl.hpp"
#include "nsISupportsID_impl.hpp"
#include "nsISupportsPRBool_impl.hpp"
#include "nsISupportsPRInt16_impl.hpp"
#include "nsISupportsPRUint16_impl.hpp"
#include "nsISupportsPRInt32_impl.hpp"
#include "nsISupportsPRUint32_impl.hpp"
#include "nsISupportsPRInt64_impl.hpp"
#include "nsISupportsPRUint64_impl.hpp"
#include "nsISupportsPRTime_impl.hpp"

//#define __XXX_TEST__

using namespace xpcom;

class nsSerializableImpl : public impl::interface_impl<nsSerializableImpl, nsISerializable>
{
public:
    nsSerializableImpl() : ref_(0)
    {
    }

    virtual ~nsSerializableImpl()
    {
    }

    XPCOM_IMETHOD query_interface(const uuid& iid, void** obj)
    {
        if (!obj)
            return NS_ERROR_NULL_POINTER;
        *obj = 0;

        if (iid == typeid_of<interface>()) {
            *obj = static_cast<nsISerializable*>(this);
        } else if (iid == typeid_of<nsISerializable>()) {
            *obj = static_cast<nsISerializable*>(this);
        } else {
            return NS_ERROR_NO_INTERFACE;
        }

        if (*obj)
            add_ref();

        return NS_OK;
    }

    XPCOM_IMETHOD_(refcnt_t) add_ref()
    {
        ++ref_;
        return ref_;
    }

    XPCOM_IMETHOD_(refcnt_t) release()
    {
        --ref_;
        if (ref_ == 0)
            delete this;
        return ref_;
    }

    void Read(noref_ptr<nsIObjectInputStream> _arg1)
    {

    }

    void Write(noref_ptr<nsIObjectOutputStream> _arg1)
    {

    }

private:
    refcnt_t ref_;
};

struct type_print
{
    template <typename T>
    void operator()(T x) const
    {
        std::cout << typeid(x).name() << std::endl;
    }
};

template <typename Types>
void print_types()
{
    typedef typename boost::mpl::transform_view<Types, boost::add_pointer<boost::mpl::_1> >::type types;
    boost::mpl::for_each<types>(type_print());
}
#ifdef __XXX_TEST__
struct AAA
{
    static int aaa;
};
int AAA::aaa = 0;

struct A0 : public object<A0>
{
};

struct A1 : public object<A1, AAA>
{
    static int print()
    {
        return aaa;
    }
};

struct A2 : public object<A2, AAA, nsIFile>
{
};

struct A3 : public object<A3,
    nsISerializable,
    nsILocalFile,
    AAA,
    nsIFile,
    nsIAtom
>
{
};

struct A4 : public object<A4,
    nsISerializable,
    nsILocalFile,
    nsIAtom,
    nsISupportsChar,
    nsISupportsVoid,
    nsISupportsCString
>
{
};

struct A5 : public object<A5,
    nsISerializable,
    nsILocalFile,
    nsIAtom,
    nsISupportsChar,
    nsISupportsVoid,
    nsISupportsCString,
    nsISupportsString,
    nsISupportsDouble,
    nsISupportsFloat,
    nsISupportsID,
    nsISupportsPRBool,
    nsISupportsPRInt16,
    nsISupportsPRInt32,
    nsISupportsPRInt64
>
{
};

typedef boost::mpl::vector<> rtypes0;
typedef boost::mpl::vector<AAA> rtypes1;
typedef boost::mpl::vector<AAA, nsIFile> rtypes2;
typedef boost::mpl::vector<
    nsISerializable,
    nsILocalFile,
    AAA,
    nsIFile,
    nsIAtom
> rtypes3;
typedef boost::mpl::vector<
    nsISerializable,
    nsILocalFile,
    nsIAtom,
    nsISupportsChar,
    nsISupportsVoid,
    nsISupportsCString,
    nsISupportsChar
> rtypes4;
typedef boost::mpl::vector<
    nsISerializable,
    nsILocalFile,
    nsIAtom,
    nsISupportsChar,
    nsISupportsVoid,
    nsISupportsCString,
    nsISupportsString,
    nsISupportsDouble,
    nsISupportsFloat,
    nsISupportsID,
    nsISupportsPRBool,
    nsISupportsPRInt16,
    nsISupportsPRInt32,
    nsISupportsPRInt64
> rtypes5;

typedef xpcom::detail::object_aux<A0, rtypes0>::all_interfaces_type atypes0;
typedef xpcom::detail::object_aux<A1, rtypes1>::all_interfaces_type atypes1;
typedef xpcom::detail::object_aux<A2, rtypes2>::all_interfaces_type atypes2;
typedef xpcom::detail::object_aux<A3, rtypes3>::all_interfaces_type atypes3;
typedef xpcom::detail::object_aux<A4, rtypes4>::all_interfaces_type atypes4;
typedef xpcom::detail::object_aux<A5, rtypes5>::all_interfaces_type atypes5;

typedef xpcom::detail::object_aux<A0, rtypes0>::normalized_interfaces_type itypes0;
typedef xpcom::detail::object_aux<A1, rtypes1>::normalized_interfaces_type itypes1;
typedef xpcom::detail::object_aux<A2, rtypes2>::normalized_interfaces_type itypes2;
typedef xpcom::detail::object_aux<A3, rtypes3>::normalized_interfaces_type itypes3;
typedef xpcom::detail::object_aux<A4, rtypes4>::normalized_interfaces_type itypes4;
typedef xpcom::detail::object_aux<A5, rtypes5>::normalized_interfaces_type itypes5;

typedef xpcom::detail::object_aux<A0, rtypes0>::custom_interfaces_type ctypes0;
typedef xpcom::detail::object_aux<A1, rtypes1>::custom_interfaces_type ctypes1;
typedef xpcom::detail::object_aux<A2, rtypes2>::custom_interfaces_type ctypes2;
typedef xpcom::detail::object_aux<A3, rtypes3>::custom_interfaces_type ctypes3;
typedef xpcom::detail::object_aux<A4, rtypes4>::custom_interfaces_type ctypes4;
typedef xpcom::detail::object_aux<A5, rtypes5>::custom_interfaces_type ctypes5;

typedef xpcom::detail::object_aux<A0, rtypes0>::normalized_all_interfaces_type htypes0;
typedef xpcom::detail::object_aux<A1, rtypes1>::normalized_all_interfaces_type htypes1;
typedef xpcom::detail::object_aux<A2, rtypes2>::normalized_all_interfaces_type htypes2;
typedef xpcom::detail::object_aux<A3, rtypes3>::normalized_all_interfaces_type htypes3;
typedef xpcom::detail::object_aux<A4, rtypes4>::normalized_all_interfaces_type htypes4;
typedef xpcom::detail::object_aux<A5, rtypes5>::normalized_all_interfaces_type htypes5;
#endif

template <typename T, typename Types>
void print_entries()
{
    typedef interface_table<T, Types> interface_table_type;
    const interface_table_entry* entries = interface_table_type::entries;
    while (entries->iid) {
        std::cout << *entries->iid << "   " << entries->offset << std::endl;
        ++entries;
    }
    std::cout << std::endl;
}

#define PRINT_TYPE(n) \
    std::cout << std::endl; \
    std::cout << "========== atypes" << n << "==========" << std::endl; \
    print_types<atypes##n>(); \
    std::cout << std::endl; \
    std::cout << "========== itypes" << n << "==========" << std::endl; \
    print_types<itypes##n>(); \
    std::cout << std::endl; \
    std::cout << "========== itable" << n << "==========" << std::endl; \
    print_types<itable##n::entries_type>(); \
    std::cout << std::endl; \
    std::cout << "========== ctypes" << n << "==========" << std::endl; \
    print_types<ctypes##n>(); \
    std::cout << std::endl; \
    std::cout << "========== htypes" << n << "==========" << std::endl; \
    print_types<htypes##n>();

BOOST_AUTO_TEST_CASE(test_def)
{
    using namespace xpcom;

    nsSerializableImpl* p = new nsSerializableImpl;
    delete p;

#ifdef __XXX_TEST__
    A1::aaa = 1;
    A2::aaa = 2;
    A3::aaa = 3;

    print_entries<A0, itypes0>();
    print_entries<A1, itypes1>();
    print_entries<A2, itypes2>();
    print_entries<A3, itypes3>();
    print_entries<A4, itypes4>();
    print_entries<A5, itypes5>();

    typedef interface_table<A0, itypes0>::type itable0;
    typedef interface_table<A1, itypes1>::type itable1;
    typedef interface_table<A2, itypes2>::type itable2;
    typedef interface_table<A3, itypes3>::type itable3;
    typedef interface_table<A4, itypes4>::type itable4;
    typedef interface_table<A5, itypes5>::type itable5;

    PRINT_TYPE(0);
    PRINT_TYPE(1);
    PRINT_TYPE(2);
    PRINT_TYPE(3);
    PRINT_TYPE(4);
    PRINT_TYPE(5);
#endif
}
